diff --git a/kpimidentities/signature.cpp b/kpimidentities/signature.cpp index 200a2d496..eb1e76ca5 100644 --- a/kpimidentities/signature.cpp +++ b/kpimidentities/signature.cpp @@ -1,586 +1,609 @@ /* Copyright (c) 2002-2004 Marc Mutz Copyright (c) 2007 Tom Albers Copyright (c) 2009 Thomas McGuire This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "signature.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace KPIMIdentities; class SignaturePrivate { public: struct EmbeddedImage { QImage image; QString name; }; typedef QSharedPointer EmbeddedImagePtr; /// List of images that belong to this signature. Either added by addImage() or /// by readConfig(). QList embeddedImages; /// The directory where the images will be saved to. QString saveLocation; }; QDataStream &operator<< ( QDataStream &stream, const SignaturePrivate::EmbeddedImagePtr &img ) { return stream << img->image << img->name; } QDataStream &operator>> ( QDataStream &stream, SignaturePrivate::EmbeddedImagePtr &img ) { return stream >> img->image >> img->name; } // TODO: KDE5: BIC: Add a real d-pointer. // This QHash is just a workaround around BIC issues, for more info see // http://techbase.kde.org/Policies/Binary_Compatibility_Issues_With_C++ typedef QHash SigPrivateHash; Q_GLOBAL_STATIC(SigPrivateHash, d_func) static SignaturePrivate* d( const Signature *sig ) { SignaturePrivate *ret = d_func()->value( sig, 0 ); if ( !ret ) { ret = new SignaturePrivate; d_func()->insert( sig, ret ); } return ret; } static void delete_d( const Signature* sig ) { SignaturePrivate *ret = d_func()->value( sig, 0 ); delete ret; d_func()->remove( sig ); } Signature::Signature() : mType( Disabled ), mInlinedHtml( false ) {} Signature::Signature( const QString &text ) : mText( text ), mType( Inlined ), mInlinedHtml( false ) {} Signature::Signature( const QString &url, bool isExecutable ) : mUrl( url ), mType( isExecutable ? FromCommand : FromFile ), mInlinedHtml( false ) {} void Signature::assignFrom ( const KPIMIdentities::Signature &that ) { mUrl = that.mUrl; mInlinedHtml = that.mInlinedHtml; mText = that.mText; mType = that.mType; d( this )->saveLocation = d( &that )->saveLocation; d( this )->embeddedImages = d( &that )->embeddedImages; } Signature::Signature ( const Signature &that ) { assignFrom( that ); } Signature& Signature::operator= ( const KPIMIdentities::Signature & that ) { if ( this == &that ) return *this; assignFrom( that ); return *this; } Signature::~Signature() { delete_d( this ); } QString Signature::rawText( bool *ok ) const { switch ( mType ) { case Disabled: if ( ok ) { *ok = true; } return QString(); case Inlined: if ( ok ) { *ok = true; } return mText; case FromFile: return textFromFile( ok ); case FromCommand: return textFromCommand( ok ); }; kFatal(5325) << "Signature::type() returned unknown value!"; return QString(); // make compiler happy } QString Signature::textFromCommand( bool *ok ) const { assert( mType == FromCommand ); // handle pathological cases: if ( mUrl.isEmpty() ) { if ( ok ) { *ok = true; } return QString(); } // create a shell process: KProcess proc; proc.setOutputChannelMode( KProcess::SeparateChannels ); proc.setShellCommand( mUrl ); int rc = proc.execute(); // handle errors, if any: if ( rc != 0 ) { if ( ok ) { *ok = false; } QString wmsg = i18n( "Failed to execute signature script

%1:

" "

%2

", mUrl, QString( proc.readAllStandardError() ) ); KMessageBox::error( 0, wmsg ); return QString(); } // no errors: if ( ok ) { *ok = true; } // get output: QByteArray output = proc.readAllStandardOutput(); // TODO: hmm, should we allow other encodings, too? return QString::fromLocal8Bit( output.data(), output.size() ); } QString Signature::textFromFile( bool *ok ) const { assert( mType == FromFile ); // TODO: Use KIO::NetAccess to download non-local files! if ( !KUrl( mUrl ).isLocalFile() && !( QFileInfo( mUrl ).isRelative() && QFileInfo( mUrl ).exists() ) ) { kDebug(5325) << "Signature::textFromFile:" << "non-local URLs are unsupported"; if ( ok ) { *ok = false; } return QString(); } if ( ok ) { *ok = true; } // TODO: hmm, should we allow other encodings, too? const QByteArray ba = KPIMUtils::kFileToByteArray( mUrl, false ); return QString::fromLocal8Bit( ba.data(), ba.size() ); } QString Signature::withSeparator( bool *ok ) const { QString signature = rawText( ok ); if ( ok && (*ok) == false ) return QString(); if ( signature.isEmpty() ) { return signature; // don't add a separator in this case } - QString newline = ( isInlinedHtml() && mType == Inlined ) ? "
" : "\n"; + const bool htmlSig = ( isInlinedHtml() && mType == Inlined ); + QString newline = htmlSig ? "
" : "\n"; + if ( htmlSig && signature.startsWith( "name; } return ret; } void Signature::cleanupImages() const { // Remove any images from the internal structure that are no longer there if ( isInlinedHtml() ) { foreach( const SignaturePrivate::EmbeddedImagePtr imageInList, d( this )->embeddedImages ) { bool found = false; foreach( const QString &imageInHtml, findImageNames( mText ) ) { if ( imageInHtml == imageInList->name ) { found = true; break; } } if ( !found ) d( this )->embeddedImages.removeAll( imageInList ); } } // Delete all the old image files if ( !d( this )->saveLocation.isEmpty() ) { QDir dir( d( this )->saveLocation ); foreach( const QString &fileName, dir.entryList( QDir::Files | QDir::NoDotAndDotDot | QDir::NoSymLinks ) ) { if ( fileName.toLower().endsWith( ".png" ) ) { kDebug() << "Deleting old image" << dir.path() + fileName; dir.remove( fileName ); } } } } void Signature::saveImages() const { if ( isInlinedHtml() && !d( this )->saveLocation.isEmpty() ) { foreach( const SignaturePrivate::EmbeddedImagePtr &image, d( this )->embeddedImages ) { QString location = d( this )->saveLocation + '/' + image->name; if ( !image->image.save( location, "PNG" ) ) { kWarning() << "Failed to save image" << location; } } } } void Signature::readConfig( const KConfigGroup &config ) { QString sigType = config.readEntry( sigTypeKey ); if ( sigType == sigTypeInlineValue ) { mType = Inlined; mInlinedHtml = config.readEntry( sigTypeInlinedHtmlKey, false ); } else if ( sigType == sigTypeFileValue ) { mType = FromFile; mUrl = config.readPathEntry( sigFileKey, QString() ); } else if ( sigType == sigTypeCommandValue ) { mType = FromCommand; mUrl = config.readPathEntry( sigCommandKey, QString() ); } else { mType = Disabled; } mText = config.readEntry( sigTextKey ); d( this )->saveLocation = config.readEntry( sigImageLocation ); if ( isInlinedHtml() && !d( this )->saveLocation.isEmpty() ) { QDir dir( d( this )->saveLocation ); foreach( const QString &fileName, dir.entryList( QDir::Files | QDir::NoDotAndDotDot | QDir::NoSymLinks ) ) { if ( fileName.toLower().endsWith( ".png" ) ) { QImage image; if ( image.load( dir.path() + '/' + fileName ) ) { addImage( image, fileName ); } else { kWarning() << "Unable to load image" << dir.path() + '/' + fileName; } } } } } void Signature::writeConfig( KConfigGroup &config ) const { switch ( mType ) { case Inlined: config.writeEntry( sigTypeKey, sigTypeInlineValue ); config.writeEntry( sigTypeInlinedHtmlKey, mInlinedHtml ); break; case FromFile: config.writeEntry( sigTypeKey, sigTypeFileValue ); config.writePathEntry( sigFileKey, mUrl ); break; case FromCommand: config.writeEntry( sigTypeKey, sigTypeCommandValue ); config.writePathEntry( sigCommandKey, mUrl ); break; case Disabled: config.writeEntry( sigTypeKey, sigTypeDisabledValue ); default: break; } config.writeEntry( sigTextKey, mText ); config.writeEntry( sigImageLocation, d( this )->saveLocation ); cleanupImages(); saveImages(); } void Signature::insertIntoTextEdit( KRichTextEdit *textEdit, Placement placement, bool addSeparator ) { - // Bah. - const_cast( this )->insertIntoTextEdit( textEdit, placement, addSeparator ); + insertIntoTextEdit( textEdit, placement, addSeparator, true ); } void Signature::insertIntoTextEdit( KRichTextEdit *textEdit, - Placement placement, bool addSeparator ) const + Placement placement, bool addSeperator, + bool addNewlines ) const { QString signature; - if ( addSeparator ) + if ( addSeperator ) signature = withSeparator(); else signature = rawText(); insertPlainSignatureIntoTextEdit( signature, textEdit, placement, ( isInlinedHtml() && - type() == KPIMIdentities::Signature::Inlined ) ); + type() == KPIMIdentities::Signature::Inlined ), + addNewlines ); // We added the text of the signature above, now it is time to add the images as well. KPIMTextEdit::TextEdit *pimEdit = dynamic_cast( textEdit ); if ( pimEdit && isInlinedHtml() ) { foreach( const SignaturePrivate::EmbeddedImagePtr &image, d( this )->embeddedImages ) { pimEdit->loadImage( image->image, image->name, image->name ); } } } void Signature::insertPlainSignatureIntoTextEdit( const QString &signature, KRichTextEdit *textEdit, Signature::Placement placement, bool isHtml ) +{ + insertPlainSignatureIntoTextEdit( signature, textEdit, placement, isHtml, true ); +} + +void Signature::insertPlainSignatureIntoTextEdit( const QString &signature, + KRichTextEdit *textEdit, + Placement placement, + bool isHtml, + bool addNewlines ) { if ( !signature.isEmpty() ) { // Save the modified state of the document, as inserting a signature // shouldn't change this. Restore it at the end of this function. bool isModified = textEdit->document()->isModified(); // Move to the desired position, where the signature should be inserted QTextCursor cursor = textEdit->textCursor(); QTextCursor oldCursor = cursor; cursor.beginEditBlock(); if ( placement == End ) cursor.movePosition( QTextCursor::End ); else if ( placement == Start ) cursor.movePosition( QTextCursor::Start ); textEdit->setTextCursor( cursor ); + + QString lineSep; + if ( addNewlines ) { + if ( isHtml ) + lineSep = QLatin1String( "
" ); + else + lineSep = QLatin1Char( '\n' ); + } + // Insert the signature and newlines depending on where it was inserted. bool hackForCursorsAtEnd = false; int oldCursorPos = -1; if ( placement == End ) { if ( oldCursor.position() == textEdit->toPlainText().length() ) { hackForCursorsAtEnd = true; oldCursorPos = oldCursor.position(); } if ( isHtml ) { - textEdit->insertHtml( QLatin1String( "
" ) + signature ); + textEdit->insertHtml( lineSep + signature ); } else { - textEdit->insertPlainText( QLatin1Char( '\n' ) + signature ); + textEdit->insertPlainText( lineSep + signature ); } } else if ( placement == Start || placement == AtCursor ) { if ( isHtml ) { - textEdit->insertHtml( QLatin1String( "
" ) + signature + QLatin1String( "
" ) ); + textEdit->insertHtml( lineSep + signature + lineSep ); } else { - textEdit->insertPlainText( QLatin1Char( '\n' ) + signature + QLatin1Char( '\n' ) ); + textEdit->insertPlainText( lineSep + signature + lineSep ); } } cursor.endEditBlock(); // There is one special case when re-setting the old cursor: The cursor // was at the end. In this case, QTextEdit has no way to know // if the signature was added before or after the cursor, and just decides // that it was added before (and the cursor moves to the end, but it should // not when appending a signature). See bug 167961 if ( hackForCursorsAtEnd ) oldCursor.setPosition( oldCursorPos ); textEdit->setTextCursor( oldCursor ); textEdit->ensureCursorVisible(); textEdit->document()->setModified( isModified ); if ( isHtml ) { textEdit->enableRichTextMode(); } } } // --------------------- Operators -------------------// QDataStream &KPIMIdentities::operator<< ( QDataStream &stream, const KPIMIdentities::Signature &sig ) { return stream << static_cast( sig.mType ) << sig.mUrl << sig.mText << d( &sig )->saveLocation << d( &sig )->embeddedImages; } QDataStream &KPIMIdentities::operator>> ( QDataStream &stream, KPIMIdentities::Signature &sig ) { quint8 s; stream >> s >> sig.mUrl >> sig.mText >> d( &sig )->saveLocation >> d( &sig )->embeddedImages; sig.mType = static_cast( s ); return stream; } bool Signature::operator== ( const Signature &other ) const { if ( mType != other.mType ) { return false; } if ( mType == Inlined && mInlinedHtml ) { if ( d( this )->saveLocation != d( &other )->saveLocation ) return false; if ( d( this )->embeddedImages != d( &other )->embeddedImages ) return false; } switch ( mType ) { case Inlined: return mText == other.mText; case FromFile: case FromCommand: return mUrl == other.mUrl; default: case Disabled: return true; } } QString Signature::plainText() const { QString sigText = rawText(); if ( isInlinedHtml() && type() == Inlined ) { // Use a QTextDocument as a helper, it does all the work for us and // strips all HTML tags. QTextDocument helper; QTextCursor helperCursor( &helper ); helperCursor.insertHtml( sigText ); sigText = helper.toPlainText(); } return sigText; } void Signature::addImage ( const QImage& imageData, const QString& imageName ) { Q_ASSERT( !( d( this )->saveLocation.isEmpty() ) ); SignaturePrivate::EmbeddedImagePtr image( new SignaturePrivate::EmbeddedImage() ); image->image = imageData; image->name = imageName; d( this )->embeddedImages.append( image ); } void Signature::setImageLocation ( const QString& path ) { d( this )->saveLocation = path; } // --------------- Getters -----------------------// QString Signature::text() const { return mText; } QString Signature::url() const { return mUrl; } Signature::Type Signature::type() const { return mType; } // --------------- Setters -----------------------// void Signature::setText( const QString &text ) { mText = text; mType = Inlined; } void Signature::setType( Type type ) { mType = type; } diff --git a/kpimidentities/signature.h b/kpimidentities/signature.h index ec5165cb2..700be095c 100644 --- a/kpimidentities/signature.h +++ b/kpimidentities/signature.h @@ -1,276 +1,292 @@ /* Copyright (c) 2002-2004 Marc Mutz Copyright (c) 2007 Tom Albers Copyright (c) 2009 Thomas McGuire Author: Stefan Taferner This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KPIMIDENTITIES_SIGNATURE_H #define KPIMIDENTITIES_SIGNATURE_H #include "kpimidentities_export.h" #include #include #include #include #include #include namespace KPIMIdentities { class Signature; class Identity; } class KConfigGroup; class KRichTextEdit; namespace KPIMIdentities { KPIMIDENTITIES_EXPORT QDataStream &operator<< ( QDataStream &stream, const KPIMIdentities::Signature &sig ); KPIMIDENTITIES_EXPORT QDataStream &operator>> ( QDataStream &stream, KPIMIdentities::Signature &sig ); /** * @short Abstraction of a signature (aka "footer"). * * The signature can either be plain text, HTML text, text returned from a command or text stored * in a file. * * In case of HTML text, the signature can contain images. * Since you set the HTML source with setText(), there also needs to be a way to add the images * to the signature, as the HTML source contains only the img tags that reference those images. * To add the image to the signature, call addImage(). The name given there must match the name * of the img tag in the HTML source. * * The images need to be stored somewhere. The Signature class handles that by storing all images * in a directory. You must set that directory with setImageLocation(), before calling addImage(). * The images added with addImage() are then saved to that directory when calling writeConfig(). * When loading a signature, readConfig() automatically loads the images as well. * To actually add the images to a text edit, call insertIntoTextEdit(). */ class KPIMIDENTITIES_EXPORT Signature { friend class Identity; friend KPIMIDENTITIES_EXPORT QDataStream &operator<< ( QDataStream &stream, const Signature &sig ); friend KPIMIDENTITIES_EXPORT QDataStream &operator>> ( QDataStream &stream, Signature &sig ); public: /** Type of signature (ie. way to obtain the signature text) */ enum Type { Disabled = 0, Inlined = 1, FromFile = 2, FromCommand = 3 }; /** * Describes the placement of the signature text when it is to be inserted into a * text edit */ enum Placement { Start, ///< The signature is placed at the start of the textedit End, ///< The signature is placed at the end of the textedit AtCursor ///< The signature is placed at the current cursor position }; /** Used for comparison */ bool operator== ( const Signature &other ) const; /** Constructor for disabled signature */ Signature(); /** Constructor for inline text */ Signature( const QString &text ); /** Constructor for text from a file or from output of a command */ Signature( const QString &url, bool isExecutable ); /** Copy constructor */ Signature( const Signature &that ); /** Assignment operator */ Signature& operator= ( const Signature &that ); /** Destructor */ ~Signature(); /** @return the raw signature text as entered resp. read from file. */ QString rawText( bool *ok=0 ) const; /** @return the signature text with a "-- \n" separator added, if necessary. A newline will not be appended or prepended. */ QString withSeparator( bool *ok=0 ) const; /** Set the signature text and mark this signature as being of "inline text" type. */ void setText( const QString &text ); QString text() const; /** * Returns the text of the signature. If the signature is HTML, the HTML * tags will be stripped. * @since 4.4 */ QString plainText() const; /** Set the signature URL and mark this signature as being of "from file" resp. "from output of command" type. */ void setUrl( const QString &url, bool isExecutable=false ); QString url() const; /// @return the type of signature (ie. way to obtain the signature text) Type type() const; void setType( Type type ); /** * Sets the inlined signature to text or html * @param isHtml sets the inlined signature to html * @since 4.1 */ void setInlinedHtml( bool isHtml ); /** * @return boolean whether the inlined signature is html * @since 4.1 */ bool isInlinedHtml() const; /** * Sets the location where the copies of the signature images will be stored. * The images will be stored there when calling writeConfig(). The image location * is stored in the config, so the next readConfig() call knows where to look for * images. * It is recommended to use KStandardDirs::locateLocal( "data", "emailidentities/%1" ) * for the location, where %1 is the unique identifier of the identity. * * @warning readConfig will delete all other PNG files in this directory, as they could * be stale inline image files * * Like with addImage(), the SignatureConfigurator will handle this for you. * * @since 4.4 */ void setImageLocation( const QString &path ); /** * Adds the given image to the signature. * This is needed if you use setText() to set some HTML source that references images. Those * referenced images needed to be added by calling this function. The @imageName has to match * the src attribute of the img tag. * * If you use SignatureConfigurator, you don't need to call this function, as the configurator * will handle this for you. * setImageLocation() needs to be called once before. * @since 4.4 */ void addImage( const QImage &image, const QString &imageName ); /** * Inserts this signature into the given text edit. * The cursor position is preserved. * A leading or trailing newline is also added automatically, depending on * the placement. * For undo/redo, this is treated as one operation. * * Rich text mode of the text edit will be enabled if the signature is in * inlined HTML format. * * If this signature uses images, they will be added automatically. * * @param textEdit the signature will be inserted into this text edit. * @param placement defines where in the text edit the signature should be * inserted. * @param addSeparator if true, the separator '-- \n' will be added in front * of the signature * * @since 4.3 * TODO: KDE5: BIC: remove, as we have a const version below * TODO: KDE5: BIC: Change from KRichTextEdit to KPIMTextEdit::TextEdit, to avoid * the dynamic_cast used here */ - void insertIntoTextEdit( KRichTextEdit *textEdit, - Placement placement = End, bool addSeparator = true ); + void KDE_DEPRECATED insertIntoTextEdit( KRichTextEdit *textEdit, + Placement placement = End, bool addSeparator = true ); /** - * Same as the other insertIntoTextEdit(), only that this is the const version + * Same as the other insertIntoTextEdit(), only that this is const and has + * an additional parameter. * @since 4.4 */ void insertIntoTextEdit( KRichTextEdit *textEdit, - Placement placement = End, bool addSeparator = true ) const; + Placement placement = End, bool addSeparator = true, + bool addNewlines = true ) const; /** * Inserts this given signature into the given text edit. * The cursor position is preserved. * A leading or trailing newline is also added automatically, depending on * the placement. * For undo/redo, this is treated as one operation. * A separator is not added. * * Use the insertIntoTextEdit() function if possible, as it has support * for separators and does HTML detection automatically. * * Rich text mode of the text edit will be enabled if @p isHtml is true. * * @param signature the signature, either as plain text or as HTML * @param textEdit the text edit to insert the signature into * @param placement defines where in the textedit the signature should be * inserted. * @param isHtml defines whether the signature should be inserted as text or html * * @since 4.3 + * TODO: KDE5: BIC: remove this method in favor of the overloaded one + */ + static void KDE_DEPRECATED insertPlainSignatureIntoTextEdit( const QString &signature, + KRichTextEdit *textEdit, + Placement placement = End, + bool isHtml = false ); + + /** + * Same as the above, with the possibility to omit linebreaks altogether + * @param addNewlines: If true, will add some newlines before or after the body, depending + * on the placement. Those newlines are useful when this function is + * triggered when the cursor is in the middle of some text. + * @since 4.4 */ static void insertPlainSignatureIntoTextEdit( const QString &signature, KRichTextEdit *textEdit, Placement placement = End, - bool isHtml = false ); + bool isHtml = false, + bool addNewlines = true ); protected: void writeConfig( KConfigGroup &config ) const; void readConfig( const KConfigGroup &config ); /** * Helper used for the copy constructor and the assignment operator */ void assignFrom( const Signature &that ); /** * Clean up unused images from our internal list and delete all images * from the file system */ void cleanupImages() const; /** * Saves all images from our internal list to the file system. */ void saveImages() const; private: QString textFromFile( bool *ok ) const; QString textFromCommand( bool *ok ) const; // TODO: KDE5: BIC: Add a d-pointer!!! // There is already a pseude private class in the .cpp, using a hash. QString mUrl; QString mText; Type mType; bool mInlinedHtml; }; } #endif /*kpim_signature_h*/ diff --git a/kpimidentities/signatureconfigurator.cpp b/kpimidentities/signatureconfigurator.cpp index 52fcda16b..4c9c8e8a0 100644 --- a/kpimidentities/signatureconfigurator.cpp +++ b/kpimidentities/signatureconfigurator.cpp @@ -1,465 +1,465 @@ /* -*- c++ -*- Copyright 2008 Thomas McGuire Copyright 2008 Edwin Schepers Copyright 2004 Marc Mutz This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #include "signatureconfigurator.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace KPIMIdentities; namespace KPIMIdentities { /** Private class that helps to provide binary compatibility between releases. @internal */ //@cond PRIVATE class SignatureConfigurator::Private { public: Private( SignatureConfigurator *parent ); void init(); SignatureConfigurator *q; bool inlinedHtml; HtmlImageMode imageMode; QString imageLocation; }; //@endcond SignatureConfigurator::Private::Private( SignatureConfigurator *parent ) :q( parent ), imageMode( DisableHtmlImages ) { } void SignatureConfigurator::Private::init() { // tmp. vars: QLabel * label; QWidget * page; QHBoxLayout * hlay; QVBoxLayout * vlay; QVBoxLayout * page_vlay; vlay = new QVBoxLayout( q ); vlay->setObjectName( "main layout" ); vlay->setMargin( 0 ); // "enable signatue" checkbox: q->mEnableCheck = new QCheckBox( i18n("&Enable signature"), q ); q->mEnableCheck->setWhatsThis( i18n("Check this box if you want KMail to append a signature to mails " "written with this identity.")); vlay->addWidget( q->mEnableCheck ); // "obtain signature text from" combo and label: hlay = new QHBoxLayout(); // inherits spacing vlay->addLayout( hlay ); q->mSourceCombo = new KComboBox( q ); q->mSourceCombo->setEditable( false ); q->mSourceCombo->setWhatsThis( i18n("Click on the widgets below to obtain help on the input methods.")); q->mSourceCombo->setEnabled( false ); // since !mEnableCheck->isChecked() q->mSourceCombo->addItems( QStringList() << i18nc("continuation of \"obtain signature text from\"", "Input Field Below") << i18nc("continuation of \"obtain signature text from\"", "File") << i18nc("continuation of \"obtain signature text from\"", "Output of Command") ); label = new QLabel( i18n("Obtain signature &text from:"), q ); label->setBuddy( q->mSourceCombo ); label->setEnabled( false ); // since !mEnableCheck->isChecked() hlay->addWidget( label ); hlay->addWidget( q->mSourceCombo, 1 ); // widget stack that is controlled by the source combo: QStackedWidget * widgetStack = new QStackedWidget( q ); widgetStack->setEnabled( false ); // since !mEnableCheck->isChecked() vlay->addWidget( widgetStack, 1 ); q->connect( q->mSourceCombo, SIGNAL(currentIndexChanged(int)), widgetStack, SLOT(setCurrentIndex (int)) ); q->connect( q->mSourceCombo, SIGNAL(highlighted(int)), widgetStack, SLOT(setCurrentIndex (int)) ); // connects for the enabling of the widgets depending on // signatureEnabled: q->connect( q->mEnableCheck, SIGNAL(toggled(bool)), q->mSourceCombo, SLOT(setEnabled(bool)) ); q->connect( q->mEnableCheck, SIGNAL(toggled(bool)), widgetStack, SLOT(setEnabled(bool)) ); q->connect( q->mEnableCheck, SIGNAL(toggled(bool)), label, SLOT(setEnabled(bool)) ); // The focus might be still in the widget that is disabled q->connect( q->mEnableCheck, SIGNAL(clicked()), q->mEnableCheck, SLOT(setFocus()) ); int pageno = 0; // page 0: input field for direct entering: page = new QWidget( widgetStack ); widgetStack->insertWidget( pageno, page ); page_vlay = new QVBoxLayout( page ); q->mEditToolBar = new KToolBar( q ); q->mEditToolBar->setToolButtonStyle( Qt::ToolButtonIconOnly ); page_vlay->addWidget( q->mEditToolBar, 0 ); q->mFormatToolBar = new KToolBar( q ); q->mFormatToolBar->setToolButtonStyle( Qt::ToolButtonIconOnly ); page_vlay->addWidget( q->mFormatToolBar, 1 ); q->mTextEdit = new KPIMTextEdit::TextEdit( q ); static_cast( q->mTextEdit )->enableImageActions(); page_vlay->addWidget( q->mTextEdit, 2 ); q->mTextEdit->setWhatsThis( i18n("Use this field to enter an arbitrary static signature.")); // exclude SupportToPlainText. q->mTextEdit->setRichTextSupport( KRichTextWidget::FullTextFormattingSupport | KRichTextWidget::FullListSupport | KRichTextWidget::SupportAlignment | KRichTextWidget::SupportRuleLine | KRichTextWidget::SupportHyperlinks | KRichTextWidget::SupportFormatPainting ); // Fill the toolbars. KActionCollection *actionCollection = new KActionCollection( q ); q->mTextEdit->createActions( actionCollection ); q->mEditToolBar->addAction( actionCollection->action( "format_text_bold" ) ); q->mEditToolBar->addAction( actionCollection->action( "format_text_italic" ) ); q->mEditToolBar->addAction( actionCollection->action( "format_text_underline" ) ); q->mEditToolBar->addAction( actionCollection->action( "format_text_strikeout" ) ); q->mEditToolBar->addAction( actionCollection->action( "format_text_foreground_color" ) ); q->mEditToolBar->addAction( actionCollection->action( "format_text_background_color" ) ); q->mEditToolBar->addAction( actionCollection->action( "format_font_family" ) ); q->mEditToolBar->addAction( actionCollection->action( "format_font_size" ) ); q->mFormatToolBar->addAction( actionCollection->action( "format_list_style" ) ); q->mFormatToolBar->addAction( actionCollection->action( "format_list_indent_more" ) ); q->mFormatToolBar->addAction( actionCollection->action( "format_list_indent_less" ) ); q->mFormatToolBar->addAction( actionCollection->action( "format_list_indent_less" ) ); q->mFormatToolBar->addSeparator(); q->mFormatToolBar->addAction( actionCollection->action( "format_align_left" ) ); q->mFormatToolBar->addAction( actionCollection->action( "format_align_center" ) ); q->mFormatToolBar->addAction( actionCollection->action( "format_align_right" ) ); q->mFormatToolBar->addAction( actionCollection->action( "format_align_justify" ) ); q->mFormatToolBar->addSeparator(); q->mFormatToolBar->addAction( actionCollection->action( "insert_horizontal_rule" ) ); q->mFormatToolBar->addAction( actionCollection->action( "manage_link" ) ); q->mFormatToolBar->addAction( actionCollection->action( "format_painter" ) ); if ( imageMode == EnableHtmlImages ) { q->mFormatToolBar->addSeparator(); q->mFormatToolBar->addAction( actionCollection->action( "add_image" ) ); } hlay = new QHBoxLayout(); // inherits spacing page_vlay->addLayout( hlay ); q->mHtmlCheck = new QCheckBox( i18n("&Use HTML"), page ); q->connect( q->mHtmlCheck, SIGNAL(clicked()), q, SLOT(slotSetHtml()) ); hlay->addWidget( q->mHtmlCheck ); inlinedHtml = true; widgetStack->setCurrentIndex( 0 ); // since mSourceCombo->currentItem() == 0 // page 1: "signature file" requester, label, "edit file" button: ++pageno; page = new QWidget( widgetStack ); widgetStack->insertWidget( pageno, page ); // force sequential numbers (play safe) page_vlay = new QVBoxLayout( page ); page_vlay->setMargin( 0 ); hlay = new QHBoxLayout(); // inherits spacing page_vlay->addLayout( hlay ); q->mFileRequester = new KUrlRequester( page ); q->mFileRequester->setWhatsThis( i18n("Use this requester to specify a text file that contains your " "signature. It will be read every time you create a new mail or " "append a new signature.")); label = new QLabel( i18n("S&pecify file:"), page ); label->setBuddy( q->mFileRequester ); hlay->addWidget( label ); hlay->addWidget( q->mFileRequester, 1 ); q->mFileRequester->button()->setAutoDefault( false ); q->connect( q->mFileRequester, SIGNAL(textChanged(const QString &)), q, SLOT(slotEnableEditButton(const QString &)) ); q->mEditButton = new QPushButton( i18n("Edit &File"), page ); q->mEditButton->setWhatsThis( i18n("Opens the specified file in a text editor.")); q->connect( q->mEditButton, SIGNAL(clicked()), q, SLOT(slotEdit()) ); q->mEditButton->setAutoDefault( false ); q->mEditButton->setEnabled( false ); // initially nothing to edit hlay->addWidget( q->mEditButton ); page_vlay->addStretch( 1 ); // spacer // page 2: "signature command" requester and label: ++pageno; page = new QWidget( widgetStack ); widgetStack->insertWidget( pageno,page ); page_vlay = new QVBoxLayout( page ); page_vlay->setMargin( 0 ); hlay = new QHBoxLayout(); // inherits spacing page_vlay->addLayout( hlay ); q->mCommandEdit = new KLineEdit( page ); q->mCommandEdit->setCompletionObject( new KShellCompletion() ); q->mCommandEdit->setAutoDeleteCompletionObject( true ); q->mCommandEdit->setWhatsThis( i18n("You can add an arbitrary command here, either with or without path " "depending on whether or not the command is in your Path. For every " "new mail, KMail will execute the command and use what it outputs (to " "standard output) as a signature. Usual commands for use with this " "mechanism are \"fortune\" or \"ksig -random\".")); label = new QLabel( i18n("S&pecify command:"), page ); label->setBuddy( q->mCommandEdit ); hlay->addWidget( label ); hlay->addWidget( q->mCommandEdit, 1 ); page_vlay->addStretch( 1 ); // spacer } SignatureConfigurator::SignatureConfigurator( QWidget * parent ) : QWidget( parent ), d( new Private( this ) ) { d->init(); } SignatureConfigurator::SignatureConfigurator( HtmlImageMode imageMode, QWidget * parent ) : QWidget( parent ), d( new Private( this ) ) { d->imageMode = imageMode; d->init(); } SignatureConfigurator::~SignatureConfigurator() { delete d; } bool SignatureConfigurator::isSignatureEnabled() const { return mEnableCheck->isChecked(); } void SignatureConfigurator::setSignatureEnabled( bool enable ) { mEnableCheck->setChecked( enable ); } Signature::Type SignatureConfigurator::signatureType() const { if ( !isSignatureEnabled() ) return Signature::Disabled; switch ( mSourceCombo->currentIndex() ) { case 0: return Signature::Inlined; case 1: return Signature::FromFile; case 2: return Signature::FromCommand; default: return Signature::Disabled; } } void SignatureConfigurator::setSignatureType( Signature::Type type ) { setSignatureEnabled( type != Signature::Disabled ); int idx = 0; switch( type ) { case Signature::Inlined: idx = 0; break; case Signature::FromFile: idx = 1; break; case Signature::FromCommand: idx = 2; break; default: idx = 0; break; }; mSourceCombo->setCurrentIndex( idx ); } void SignatureConfigurator::setInlineText( const QString & text ) { mTextEdit->setTextOrHtml( text ); } QString SignatureConfigurator::fileURL() const { QString file = mFileRequester->url().path(); // Force the filename to be relative to ~ instead of $PWD depending // on the rest of the code (KRun::run in Edit and KFileItem on save) if ( !file.isEmpty() && QFileInfo( file ).isRelative() ) file = QDir::home().absolutePath() + QDir::separator() + file; return file; } void SignatureConfigurator::setFileURL( const QString & url ) { mFileRequester->setUrl( url ); } QString SignatureConfigurator::commandURL() const { return mCommandEdit->text(); } void SignatureConfigurator::setCommandURL( const QString & url ) { mCommandEdit->setText( url ); } Signature SignatureConfigurator::signature() const { Signature sig; const Signature::Type sigType = signatureType(); switch ( sigType ) { case Signature::Inlined: sig.setInlinedHtml( d->inlinedHtml ); sig.setText( d->inlinedHtml ? asCleanedHTML() : mTextEdit->textOrHtml() ); if ( d->inlinedHtml ) { if ( !d->imageLocation.isEmpty() ) sig.setImageLocation( d->imageLocation ); KPIMTextEdit::ImageWithNameList images = static_cast< KPIMTextEdit::TextEdit*>( mTextEdit )->imagesWithName(); foreach( const KPIMTextEdit::ImageWithNamePtr &image, images ) { sig.addImage( image->image, image->name ); } } break; case Signature::FromCommand: sig.setUrl( commandURL(), true ); break; case Signature::FromFile: sig.setUrl( fileURL(), false ); break; case Signature::Disabled: /* do nothing */ break; } sig.setType( sigType ); return sig; } void SignatureConfigurator::setSignature( const Signature & sig ) { setSignatureType( sig.type() ); if ( sig.isInlinedHtml() ) mHtmlCheck->setCheckState( Qt::Checked ); else mHtmlCheck->setCheckState( Qt::Unchecked ); slotSetHtml(); // Let insertIntoTextEdit() handle setting the text, as that function also adds the images. mTextEdit->clear(); - sig.insertIntoTextEdit( mTextEdit, KPIMIdentities::Signature::Start, false /* no seperator*/ ); + sig.insertIntoTextEdit( mTextEdit, KPIMIdentities::Signature::Start, false, false ); if ( sig.type() == Signature::FromFile ) setFileURL( sig.url() ); else setFileURL( QString() ); if ( sig.type() == Signature::FromCommand ) setCommandURL( sig.url() ); else setCommandURL( QString() ); } void SignatureConfigurator::slotEnableEditButton( const QString & url ) { mEditButton->setDisabled( url.trimmed().isEmpty() ); } void SignatureConfigurator::slotEdit() { QString url = fileURL(); // slotEnableEditButton should prevent this assert from being hit: assert( !url.isEmpty() ); (void)KRun::runUrl( KUrl( url ), QString::fromLatin1("text/plain"), this ); } QString SignatureConfigurator::asCleanedHTML() const { QString text = mTextEdit->toHtml(); // Beautiful little hack to find the html headers produced by Qt. QTextDocument textDocument; QString html = textDocument.toHtml(); // Now remove each line from the text, the result is clean html. foreach( const QString& line, html.split( '\n' ) ){ text.remove( line + '\n' ); } return text; } // "use HTML"-checkbox (un)checked void SignatureConfigurator::slotSetHtml() { if ( mHtmlCheck->checkState() == Qt::Unchecked ) { mHtmlCheck->setText( i18n("&Use HTML") ); mEditToolBar->setVisible( false ); mEditToolBar->setEnabled( false ); mFormatToolBar->setVisible( false ); mFormatToolBar->setEnabled( false ); mTextEdit->switchToPlainText(); d->inlinedHtml = false; } else { mHtmlCheck->setText( i18n("&Use HTML (disabling removes formatting)") ); d->inlinedHtml = true; mEditToolBar->setVisible( true ); mEditToolBar->setEnabled( true ); mFormatToolBar->setVisible( true ); mFormatToolBar->setEnabled( true ); mTextEdit->enableRichTextMode(); } } void SignatureConfigurator::setImageLocation ( const QString& path ) { d->imageLocation = path; } } #include "signatureconfigurator.moc" diff --git a/kpimidentities/tests/signaturetest.cpp b/kpimidentities/tests/signaturetest.cpp index ffb873631..f39062334 100644 --- a/kpimidentities/tests/signaturetest.cpp +++ b/kpimidentities/tests/signaturetest.cpp @@ -1,229 +1,245 @@ /* Copyright (c) 20089 Thomas McGuire This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "qtest_kde.h" #include "signaturetest.h" #include "../signature.h" #include "kpimtextedit/textedit.h" #include #include #include using namespace KPIMIdentities; using namespace KPIMTextEdit; QTEST_KDEMAIN( SignatureTester, GUI ) void SignatureTester::testSignatures() { Signature sig1; sig1.setText( "Hello World" ); QCOMPARE( sig1.text(), QString( "Hello World" ) ); QCOMPARE( sig1.type(), Signature::Inlined ); QCOMPARE( sig1.rawText(), QString( "Hello World" ) ); QVERIFY( !sig1.isInlinedHtml() ); QCOMPARE( sig1.withSeparator(), QString( "-- \nHello World" ) ); Signature sig2; sig2.setText( "Hello World" ); sig2.setInlinedHtml( true ); QVERIFY( sig2.isInlinedHtml() ); QCOMPARE( sig2.type(), Signature::Inlined ); QCOMPARE( sig2.rawText(), QString( "Hello World" ) ); QCOMPARE( sig2.withSeparator(), QString( "--
Hello World" ) ); // Read this very file in, we use it for the tests QFile thisFile( __FILE__ ); thisFile.open( QIODevice::ReadOnly ); QString fileContent = QString::fromUtf8( thisFile.readAll() ); Signature sig3; sig3.setUrl( QString( "cat " ) + QString( __FILE__ ), true ); QCOMPARE( sig3.rawText(), fileContent ); QVERIFY( !sig3.isInlinedHtml() ); QVERIFY( sig3.text().isEmpty() ); QCOMPARE( sig3.type(), Signature::FromCommand ); QCOMPARE( sig3.withSeparator(), QString( "-- \n" ) + fileContent ); Signature sig4; sig4.setUrl( __FILE__, false ); QCOMPARE( sig4.rawText(), fileContent ); QVERIFY( !sig4.isInlinedHtml() ); QVERIFY( sig4.text().isEmpty() ); QCOMPARE( sig4.type(), Signature::FromFile ); QCOMPARE( sig4.withSeparator(), QString( "-- \n" ) + fileContent ); } static void setCursorPos( QTextEdit &edit, int pos ) { QTextCursor cursor( edit.document() ); cursor.setPosition( pos ); edit.setTextCursor( cursor ); } void SignatureTester::testTextEditInsertion() { TextEdit edit; Signature sig; sig.setText( "Hello World" ); // Test inserting signature at start, with seperators edit.setPlainText( "Bla Bla" ); - sig.insertIntoTextEdit( &edit, Signature::Start, true ); + sig.insertIntoTextEdit( &edit, Signature::Start, true, true ); QVERIFY( edit.textMode() == KRichTextEdit::Plain ); QCOMPARE( edit.toPlainText(), QString( "\n-- \nHello World\nBla Bla" ) ); // Test inserting signature at end. make sure cursor position is preserved edit.clear(); edit.setPlainText( "Bla Bla" ); setCursorPos( edit, 4 ); - sig.insertIntoTextEdit( &edit, Signature::End, true ); + sig.insertIntoTextEdit( &edit, Signature::End, true, true ); QCOMPARE( edit.toPlainText(), QString( "Bla Bla\n-- \nHello World" ) ); QCOMPARE( edit.textCursor().position(), 4 ); // test inserting a signature at cursor position. make sure the cursor // moves the position correctly. make sure modified state is preserved edit.clear(); edit.setPlainText( "Bla Bla" ); setCursorPos( edit, 4 ); edit.document()->setModified( false ); - sig.insertIntoTextEdit( &edit, Signature::AtCursor, true ); + sig.insertIntoTextEdit( &edit, Signature::AtCursor, true, true ); QCOMPARE( edit.toPlainText(), QString( "Bla \n-- \nHello World\nBla" ) ); QCOMPARE( edit.textCursor().position(), 21 ); QVERIFY( !edit.document()->isModified() ); // make sure undo undoes everything in one go edit.undo(); QCOMPARE( edit.toPlainText(), QString( "Bla Bla" ) ); // test inserting signature without seperator. // make sure cursor position and modified state is preserved. edit.clear(); edit.setPlainText( "Bla Bla" ); setCursorPos( edit, 4 ); edit.document()->setModified( true ); - sig.insertIntoTextEdit( &edit, Signature::End, false ); + sig.insertIntoTextEdit( &edit, Signature::End, false, true ); QCOMPARE( edit.toPlainText(), QString( "Bla Bla\nHello World" ) ); QCOMPARE( edit.textCursor().position(), 4 ); QVERIFY( edit.document()->isModified() ); sig.setText( "Hello
World" ); sig.setInlinedHtml( true ); // test that html signatures turn html on and have correct line endings (
vs \n) edit.clear(); edit.setPlainText( "Bla Bla" ); - sig.insertIntoTextEdit( &edit, Signature::End, true ); + sig.insertIntoTextEdit( &edit, Signature::End, true, true ); QVERIFY( edit.textMode() == KRichTextEdit::Rich ); QCOMPARE( edit.toPlainText(), QString( "Bla Bla\n-- \nHello\nWorld" ) ); } void SignatureTester::testBug167961() { TextEdit edit; Signature sig; sig.setText( "BLA" ); // Test that the cursor is still at the start when appending a sig into // an empty text edit - sig.insertIntoTextEdit( &edit, Signature::End, true ); + sig.insertIntoTextEdit( &edit, Signature::End, true, true ); QCOMPARE( edit.textCursor().position(), 0 ); // OTOH, when prepending a sig, the cursor should be at the end edit.clear(); - sig.insertIntoTextEdit( &edit, Signature::Start, true ); + sig.insertIntoTextEdit( &edit, Signature::Start, true, true ); QCOMPARE( edit.textCursor().position(), 9 ); // "\n-- \nBLA\n" } // Make writeConfig() public, we need it class MySignature : public Signature { public: using Signature::writeConfig; using Signature::readConfig; }; void SignatureTester::testImages() { TextEdit edit; QString image1Path = KIconLoader::global()->iconPath( QLatin1String( "folder-new" ), KIconLoader::Small, false ); QString image2Path = KIconLoader::global()->iconPath( QLatin1String( "arrow-up" ), KIconLoader::Small, false ); QImage image1, image2; QVERIFY( image1.load( image1Path ) ); QVERIFY( image2.load( image1Path ) ); QString path = KStandardDirs::locateLocal( "data", "emailidentities/unittest/" ); QString configPath = KStandardDirs::locateLocal( "config", "signaturetest" ); KConfig config( configPath ); KConfigGroup group1 = config.group( "Signature1" ); MySignature sig; sig.setImageLocation( path ); sig.setInlinedHtml( true ); sig.setText( "BlaBla" ); sig.addImage( image1, "folder-new.png" ); sig.writeConfig( group1 ); // OK, signature saved, the image should be saved as well QDir dir( path ); QStringList entryList = dir.entryList( QDir::Files | QDir::NoDotAndDotDot | QDir::NoSymLinks ); QCOMPARE( entryList.count(), 1 ); QCOMPARE( entryList.first(), QString( "folder-new.png" ) ); // Now, set the text of the signature to something without images, then save it. // The signature class should have removed the images. sig.setText( "ascii ribbon campaign - against html mail" ); sig.writeConfig( group1 ); entryList = dir.entryList( QDir::Files | QDir::NoDotAndDotDot | QDir::NoSymLinks ); QCOMPARE( entryList.count(), 0 ); // Enable images again, this time with two of the buggers sig.setText( "BlaBlaBla" ); sig.addImage( image1, "folder-new.png" ); sig.addImage( image2, "arrow-up.png" ); sig.writeConfig( group1 ); entryList = dir.entryList( QDir::Files | QDir::NoDotAndDotDot | QDir::NoSymLinks ); QCOMPARE( entryList.count(), 2 ); QCOMPARE( entryList.first(), QString( "arrow-up.png" ) ); QCOMPARE( entryList.last(), QString( "folder-new.png" ) ); // Now, create a second signature instance from the same config, and make sure it can still // read the images, and it does not mess up MySignature sig2; sig2.readConfig( group1 ); - sig2.insertIntoTextEdit( &edit ); + sig2.insertIntoTextEdit( &edit, KPIMIdentities::Signature::End, true, true ); QCOMPARE( edit.embeddedImages().count(), 2 ); QCOMPARE( sig2.text(), QString( "BlaBlaBla") ); sig2.writeConfig( group1 ); entryList = dir.entryList( QDir::Files | QDir::NoDotAndDotDot | QDir::NoSymLinks ); QCOMPARE( entryList.count(), 2 ); QCOMPARE( entryList.first(), QString( "arrow-up.png" ) ); QCOMPARE( entryList.last(), QString( "folder-new.png" ) ); // Remove one image from the signature, and make sure only 1 file is left one file system. sig2.setText( "" ); sig2.writeConfig( group1 ); edit.clear(); - sig2.insertIntoTextEdit( &edit ); + sig2.insertIntoTextEdit( &edit, Signature::End, true, true ); QCOMPARE( edit.embeddedImages().size(), 1 ); entryList = dir.entryList( QDir::Files | QDir::NoDotAndDotDot | QDir::NoSymLinks ); QCOMPARE( entryList.count(), 1 ); } +void SignatureTester::testLinebreaks() +{ + Signature sig; + sig.setType( Signature::Inlined ); + sig.setInlinedHtml( true ); + sig.setText( "Hans Mustermann
Musterstr. 42" ); + + KPIMTextEdit::TextEdit edit; + sig.insertIntoTextEdit( &edit, Signature::Start, false, false ); + QCOMPARE( edit.toPlainText(), QString( "Hans Mustermann\nMusterstr. 42" ) ); + + sig.setText( "

Hans Mustermann


Musterstr. 42" ); + sig.insertIntoTextEdit( &edit, Signature::Start, true, false ); + QCOMPARE( edit.toPlainText(), QString( "-- \nHans Mustermann\nMusterstr. 42" ) ); +} + diff --git a/kpimidentities/tests/signaturetest.h b/kpimidentities/tests/signaturetest.h index daaff9b69..8c942e25d 100644 --- a/kpimidentities/tests/signaturetest.h +++ b/kpimidentities/tests/signaturetest.h @@ -1,36 +1,37 @@ /* Copyright (c) 2009 Thomas McGuire This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef SIGNATURETEST_H #define SIGNATURETEST_H #include class SignatureTester : public QObject { Q_OBJECT private slots: void testSignatures(); void testTextEditInsertion(); void testBug167961(); void testImages(); + void testLinebreaks(); }; #endif